/*
 * cudanwGlobalMemory.cpp
 *
 *  Created on: Jan 7, 2010
 *      Author: daniel
 */

namespace CUDAN
{
	// Default Constructor.
	template<typename T, int D>
	GlobalMemory<T,D>::GlobalMemory() : DeviceMemory<T,D>()
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>::GlobalMemory()" << std::endl;
#endif
	}

	//
	template<typename T, int D>
	GlobalMemory<T,D>::GlobalMemory(HostMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>::GlobalMemory(HostMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);
	}

	template<typename T, int D>
	GlobalMemory<T,D>::GlobalMemory(DeviceMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>::GlobalMemory(DeviceMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);
	}

	template<typename T, int D>
	GlobalMemory<T,D>::GlobalMemory(GlobalMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>::GlobalMemory(GlobalMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);
	}

	template<typename T, int D>
	GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(HostMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(HostMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);

		return this;
	}

	template<typename T, int D>
	GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(DeviceMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(DeviceMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);

		return this;
	}

	template<typename T, int D>
	GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(GlobalMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>* GlobalMemory<T,D>::operator=(GlobalMemory<T,D>* copy)" << std::endl;
#endif
		this->copyFrom(copy);

		return this;
	}

	template<typename T, int D>
	GlobalMemory<T,D>::~GlobalMemory()
	{
#ifdef _DEBUG
		std::cout << "GlobalMemory<T,D>::~GlobalMemory()" << std::endl;
#endif
	}

	template<typename T, int D>
	void GlobalMemory<T,D>::freeMemory()
	{
#ifdef _DEBUG
		std::cout << "void GlobalMemory<T,D>::freeMemory()" << std::endl;
#endif
		cudaFree(this->memory);
		cudaFree(this->sizeDimGPU);
		this->memory = NULL;
		this->size = 0;
		this->sizeDim = DimensionSizeVector(D);
	}

	template<typename T, int D>
	void GlobalMemory<T,D>::print()
	{
#ifdef _DEBUG
		std::cout << "void GlobalMemory<T,D>::print()" << std::endl;
#endif
		HostMemory<T,D> h(this);
		h.print();
		h.freeMemory();
	}

	template<typename T, int D>
	void GlobalMemory<T,D>::print(DimensionSizeVector& dim)
	{
#ifdef _DEBUG
		std::cout << "void GlobalMemory<T,D>::print(DimensionSizeVector& dim)" << std::endl;
#endif
		HostMemory<T,D> h(this);
		h.print(dim);
		h.freeMemory();
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::allocateMemory(DimensionSizeVector& dim)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::allocateMemory(DimensionSizeVector& dim)" << std::endl;
#endif
		this->setSize(dim);

		if(cudaMalloc((void**)&this->memory, sizeof(T) * this->size) == cudaSuccess) return true;
		else {
			this->freeMemory();
			return false;
		}
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyFrom(HostMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyFrom(HostMemory<T,D>* copy)" << std::endl;
#endif
		if(this->size != copy->size) {
			if(this->memory != NULL) this->freeMemory();
			this->allocateMemory(copy->sizeDim);
		}

		if(cudaMemcpy(this->memory, copy->memory, sizeof(T) * copy->size, cudaMemcpyHostToDevice) == cudaSuccess)	return true;
		else return false;
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyFrom(DeviceMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyFrom(DeviceMemory<T,D>* copy)" << std::endl;
#endif
		return copy->copyTo(this);
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyFrom(GlobalMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyFrom(GlobalMemory<T,D>* copy)" << std::endl;
#endif
		if(this->size != copy->size) {
			if(this->memory != NULL) this->freeMemory();
			this->allocateMemory(copy->sizeDim);
		}

		if(cudaMemcpy(this->memory, copy->memory, sizeof(T) * copy->size, cudaMemcpyDeviceToDevice) == cudaSuccess)	return true;
		else return false;
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyTo(HostMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyTo(HostMemory<T,D>* copy)" << std::endl;
#endif
		if(this->size != copy->size) {
			if(copy->memory != NULL) copy->freeMemory();
			copy->allocateMemory(this->sizeDim);
		}

		if(cudaMemcpy(copy->memory, this->memory, sizeof(T) * this->size, cudaMemcpyDeviceToHost) == cudaSuccess)	return true;
		else return false;
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyTo(DeviceMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyTo(DeviceMemory<T,D>* copy)" << std::endl;
#endif
		return copy->copyFrom(this);
	}

	template<typename T, int D>
	bool GlobalMemory<T,D>::copyTo(GlobalMemory<T,D>* copy)
	{
#ifdef _DEBUG
		std::cout << "bool GlobalMemory<T,D>::copyTo(GlobalMemory<T,D>* copy)" << std::endl;
#endif
		if(this->size != copy->size) {
			if(copy->memory != NULL) copy->freeMemory();
			copy->allocateMemory(this->sizeDim);
		}

		if(cudaMemcpy(copy->memory, this->memory, sizeof(T) * this->size, cudaMemcpyDeviceToDevice) == cudaSuccess)	return true;
		else return false;
	}
}
